using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using SqlSugar;
using SqlInjectionScanner.Config;
using SqlInjectionScanner.Core;
using SqlInjectionScanner.Models;
using SqlInjectionScanner.Utils;
using System.Configuration;

namespace SqlInjectionScanner
{
    class Program
    {
        static async Task Main(string[] args)
        {
            Logger.Information("=== SQL注入检测工具 ===");
            Logger.Information("该工具用于检查数据库中各表是否有被SQL注入的迹象");
            Logger.Information("");

            try
            {
                // 初始化日志
                string logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "scan.log");
                try
                {
                    Directory.CreateDirectory(Path.GetDirectoryName(logPath)!);
                    Logger.Initialize(logPath);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"初始化日志时发生错误: {ex.Message}");
                    return;
                }

                // 获取数据库配置
                var config = await GetDatabaseConfigAsync(args);
                if (config == null)
                {
                    return;
                }

                // 初始化数据库连接
                var db = new SqlSugarScope(new List<ConnectionConfig>()
                {
                    new ConnectionConfig()
                    {
                        ConnectionString = config.ConnectionString,
                    DbType = config.DatabaseType switch
                    {
                        "SqlServer" => DbType.SqlServer,
                        "MySql" => DbType.MySql,
                        "PostgreSql" => DbType.PostgreSQL,
                        "SQLite" => DbType.Sqlite,
                        "Oracle" => DbType.Oracle,
                        _ => DbType.SqlServer
                    },
                        IsAutoCloseConnection = true
                    }
                });

                // 创建测试表
                db.CodeFirst.InitTables<InjectionRule>();
                // 插入规则数据
                var rules = new ConfigurationBuilder()
                    .AddJsonFile("appsettings.json")
                    .Build()
                    .GetSection("InjectionPatterns")
                    .Get<List<InjectionRule>>();
                db.Insertable(rules).ExecuteCommand();

                //// 插入测试数据
                //db.CodeFirst.InitTables<InjectionTestData>();
                //var testData = new List<InjectionTestData>
                //{
                //    new InjectionTestData { Content = "admin'--", Type = "SQL" },
                //    new InjectionTestData { Content = "<script>alert(1)</script>", Type = "XSS" }
                //};
                //db.Insertable(testData).ExecuteCommand();

                // 创建扫描器
                var scanner = new DatabaseScanner(config);

                Console.WriteLine("开始扫描数据库...");
                Console.WriteLine();

                // 执行扫描
                var (results, statistics) = await scanner.ScanAsync();

                // 生成报告
                var reportGenerator = new ReportGenerator();
                reportGenerator.GenerateConsoleReport(results, statistics);

                // 保存报告
                string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
                string reportsDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "reports");
                Directory.CreateDirectory(reportsDir);

                string csvPath = Path.Combine(reportsDir, $"sql_injection_report_{timestamp}.csv");
                reportGenerator.GenerateCsvReport(results, statistics, csvPath);
                Console.WriteLine($"CSV报告已保存至: {csvPath}");

                string htmlPath = Path.Combine(reportsDir, $"sql_injection_report_{timestamp}.html");
                reportGenerator.GenerateHtmlReport(results, statistics, htmlPath);
                Console.WriteLine($"HTML报告已保存至: {htmlPath}");
            }
            catch (Exception ex)
            {
                Logger.Error($"发生错误: {ex.Message}");
                Logger.Error(ex, "程序执行过程中发生错误");
            }
        }

        /// <summary>
        /// 获取数据库配置
        /// </summary>
        /// <param name="args">命令行参数</param>
        /// <returns>数据库配置</returns>
        private static async Task<DatabaseConfig?> GetDatabaseConfigAsync(string[] args)
        {
            // 尝试从配置文件加载
            var config = LoadConfigFromFile();

            // 如果没有配置文件或配置不完整，则通过交互方式获取
            if (config == null || string.IsNullOrEmpty(config.ConnectionString))
            {
                config = await GetConfigFromUserInputAsync();
            }

            return config;
        }

        /// <summary>
        /// 从配置文件加载配置
        /// </summary>
        /// <returns>数据库配置</returns>
        private static DatabaseConfig? LoadConfigFromFile()
        {
            try
            {
                string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json");
                if (!File.Exists(configPath))
                {
                    return null;
                }

                var configuration = new ConfigurationBuilder()
                    .AddJsonFile(configPath)
                    .Build();

                var config = new DatabaseConfig();
                configuration.GetSection("Database").Bind(config);

                if (string.IsNullOrEmpty(config.ConnectionString))
                {
                    return null;
                }

                return config;
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "加载配置文件时发生错误");
                return null;
            }
        }

        /// <summary>
        /// 通过用户输入获取配置
        /// </summary>
        /// <returns>数据库配置</returns>
        private static async Task<DatabaseConfig?> GetConfigFromUserInputAsync()
        {
            try
            {
                var config = new DatabaseConfig();

                Console.WriteLine("请选择数据库类型:");
                Console.WriteLine("1. SQL Server");
                Console.WriteLine("2. MySQL");
                Console.WriteLine("3. PostgreSQL");
                Console.WriteLine("4. SQLite");
                Console.WriteLine("5. Oracle");
                int dbTypeChoice;
                do
                {
                    Console.Write("请输入选项 (1-5): ");
                } while (!int.TryParse(Console.ReadLine(), out dbTypeChoice) || dbTypeChoice < 1 || dbTypeChoice > 5);
                
                config.DatabaseType = dbTypeChoice switch
                {
                    1 => "SqlServer",
                    2 => "MySql",
                    3 => "PostgreSql",
                    4 => "SQLite",
                    5 => "Oracle",
                    _ => "SqlServer"
                };
                
                string connectionStringExample = config.DatabaseType switch
                {
                    "SqlServer" => "Server=localhost;Database=mydb;User Id=sa;Password=password;TrustServerCertificate=True;",
                    "MySql" => "Server=localhost;Database=mydb;Uid=root;Pwd=password;",
                    "PostgreSql" => "Host=localhost;Database=mydb;Username=postgres;Password=password;",
                    "SQLite" => "Data Source=mydb.sqlite;",
                    "Oracle" => "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=ORCL)));User Id=system;Password=password;",
                    _ => "Server=localhost;Database=mydb;User Id=sa;Password=password;"
                };

                Console.WriteLine($"连接字符串示例: {connectionStringExample}");
                Console.Write("请输入连接字符串: ");
                config.ConnectionString = Console.ReadLine() ?? "";

                if (string.IsNullOrEmpty(config.ConnectionString))
                {
                    Logger.Error("连接字符串不能为空");
                    return null;
                }

                // 验证连接字符串格式
                if (!ValidateConnectionString(config.ConnectionString, config.DatabaseType))
                {
                    Logger.Error("连接字符串格式不正确");
                    return null;
                }

                Console.Write("请输入要扫描的数据库名称 (可选，留空则扫描所有可访问的数据库): ");
                string? dbName = Console.ReadLine();
                if (!string.IsNullOrEmpty(dbName))
                {
                    config.DatabaseName = dbName;
                }

                Console.Write("请输入要扫描的表名，多个表用逗号分隔 (可选，留空则扫描所有表): ");
                string? tableNames = Console.ReadLine();
                if (!string.IsNullOrEmpty(tableNames))
                {
                    config.TableNames = new List<string>(tableNames.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
                }

                Console.Write("请输入要排除的表名，多个表用逗号分隔 (可选): ");
                string? excludedTables = Console.ReadLine();
                if (!string.IsNullOrEmpty(excludedTables))
                {
                    config.ExcludedTables = new List<string>(excludedTables.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries));
                }

                Console.Write("请输入每个表最大扫描记录数 (默认10000): ");
                string? maxRecordsStr = Console.ReadLine();
                if (!string.IsNullOrEmpty(maxRecordsStr) && int.TryParse(maxRecordsStr, out int maxRecords))
                {
                    config.MaxRecordsPerTable = maxRecords;
                }

                // 测试连接
                Console.WriteLine("正在测试数据库连接...");
                var connectionManager = new ConnectionManager(config);
                bool connectionSuccess = connectionManager.TestConnection();

                if (!connectionSuccess)
                {
                    Console.WriteLine("无法连接到数据库，请检查连接字符串");
                    return null;
                }

                Console.WriteLine("数据库连接成功");

                // 保存配置
                await SaveConfigToFileAsync(config);

                return config;
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "获取用户输入时发生错误");
                Logger.Error($"发生错误: {ex.Message}");
                return null;
            }
        }

        /// <summary>
        /// 保存配置到文件
        /// </summary>
        /// <param name="config">数据库配置</param>
        private static bool ValidateConnectionString(string connectionString, string databaseType)
        {
            try
            {
                // 基本验证
                if (string.IsNullOrWhiteSpace(connectionString))
                    return false;

                // 根据数据库类型进行特定验证
                switch (databaseType)
                {
                    case "SqlServer":
                        return connectionString.Contains("Server=") || connectionString.Contains("Data Source=");
                    case "MySql":
                        return connectionString.Contains("Server=") || connectionString.Contains("Host=");
                    case "PostgreSql":
                        return connectionString.Contains("Host=");
                    case "SQLite":
                        return connectionString.Contains("Data Source=");
                    case "Oracle":
                        return connectionString.Contains("Data Source=") || connectionString.Contains("Host=");
                    default:
                        return true;
                }
            }
            catch
            {
                return false;
            }
        }

        private static async Task SaveConfigToFileAsync(DatabaseConfig config)
        {
            try
            {
                Console.Write("是否保存配置到文件? (y/n): ");
                string? response = Console.ReadLine();

                if (string.IsNullOrEmpty(response) || response.ToLower() != "y")
                {
                    return;
                }

                string configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json");
                string configJson = $@"{{
  ""Database"": {{
    ""DatabaseType"": ""{config.DatabaseType}"",
    ""ConnectionString"": ""{config.ConnectionString.Replace("\"", "\\\"")}"",
    ""DatabaseName"": ""{(config.DatabaseName ?? "")}"",
    ""MaxRecordsPerTable"": {config.MaxRecordsPerTable},
    ""BatchSize"": {config.BatchSize},
    ""TimeoutSeconds"": {config.TimeoutSeconds}
  }}
}}";

                await File.WriteAllTextAsync(configPath, configJson);
                Console.WriteLine($"配置已保存到: {configPath}");
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "保存配置文件时发生错误");
                Console.WriteLine($"保存配置文件时发生错误: {ex.Message}");
            }
        }
    }
}